package com.bins.dentist.clinic.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bins.dentist.clinic.constants.CommonConstants;
import com.bins.dentist.clinic.mapper.MenuMapper;
import com.bins.dentist.clinic.model.dto.MenuDto;
import com.bins.dentist.clinic.model.dto.MenuPageDto;
import com.bins.dentist.clinic.entity.MenuEntity;
import com.bins.dentist.clinic.model.vo.LeftMenuTreeVo;
import com.bins.dentist.clinic.model.vo.MenuTreeVo;
import com.bins.dentist.clinic.model.vo.MenuVo;
import com.bins.dentist.clinic.service.MenuService;
import com.google.common.collect.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author bins
 * @since 2023-02-20 20:53:31
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, MenuEntity> implements MenuService {

    @Autowired
    private MenuMapper menuMapper;


    @Override
    public IPage<MenuVo> getPage(MenuPageDto pageDto) {
        LambdaQueryWrapper<MenuEntity> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(!ObjectUtils.isEmpty(pageDto.getMenuName()), MenuEntity::getMenuName, pageDto.getMenuName())
                .eq(!ObjectUtils.isEmpty(pageDto.getMenuLevel()), MenuEntity::getMenuLevel, pageDto.getMenuLevel())
                .eq(MenuEntity::getIsDel, CommonConstants.DELETED_NO);
        IPage page = menuMapper.selectPage(new Page(pageDto.getPageNum(), pageDto.getPageSize()), queryWrapper);
        List<MenuEntity> userPageList = page.getRecords();
        if (CollectionUtils.isEmpty(userPageList)) {
            return page;
        }
        List<MenuVo> userList = Lists.newArrayListWithCapacity(userPageList.size());
        for (MenuEntity userEntity : userPageList) {
            MenuVo userVo = new MenuVo();
            BeanUtils.copyProperties(userEntity, userVo);
            userList.add(userVo);
        }
        page.setRecords(userList);
        return page;
    }

    @Override
    public MenuVo getDetailById(MenuDto dto) {
        MenuEntity menuEntity = menuMapper.selectById(dto.getId());
        if (ObjectUtils.isEmpty(menuEntity)) {
            return null;
        }
        MenuVo menuVo = new MenuVo();
        BeanUtils.copyProperties(menuEntity, menuVo);
        return menuVo;
    }

    @Override
    public MenuVo saveMenu(MenuDto dto) {
        Integer menuLevel;
        if (ObjectUtils.isEmpty(dto.getPid()) || dto.getPid() == 0) {
            menuLevel = 1;
        } else {
            MenuEntity parentMenu = findParentMenu(dto);
            menuLevel = parentMenu.getMenuLevel() + 1;
        }
        MenuEntity menuEntity = new MenuEntity();
        BeanUtils.copyProperties(dto, menuEntity);
        menuEntity.setCreateTime(LocalDateTime.now());
        menuEntity.setUpdateTime(LocalDateTime.now());
        menuEntity.setMenuLevel(menuLevel);
        menuEntity.setCreateBy(1L);
        menuEntity.setUpdateBy(1L);
        menuEntity.setCommand(dto.getCommand());
        if (ObjectUtils.isEmpty(dto.getId())) {
            menuMapper.insert(menuEntity);
        } else {
            menuMapper.updateById(menuEntity);
        }
        MenuVo vo = new MenuVo();
        vo.setId(menuEntity.getId());
        vo.setMenuName(menuEntity.getMenuName());
        return vo;
    }

    private MenuEntity findParentMenu(MenuDto dto) {
        QueryWrapper<MenuEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(!ObjectUtils.isEmpty(dto.getPid()), MenuEntity::getId, dto.getPid());
        return menuMapper.selectOne(queryWrapper);
    }

    @Override
    public Integer delMenu(MenuDto dto) {
        MenuEntity menuEntity = new MenuEntity();
        menuEntity.setId(dto.getId());
        menuEntity.setIsDel(1);
        return menuMapper.updateById(menuEntity);
    }

    @Override
    public List<MenuVo> userMenuList(MenuDto dto) {
        if (ObjectUtils.isEmpty(dto.getMenuLevel())) {
            dto.setMenuLevel(0);
        }
        QueryWrapper<MenuEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MenuEntity::getMenuLevel, dto.getMenuLevel())
                .eq(!ObjectUtils.isEmpty(dto.getMenuName()), MenuEntity::getMenuName, dto.getMenuName())
                .eq(!ObjectUtils.isEmpty(dto.getPid()), MenuEntity::getPid, dto.getPid());

        List<MenuEntity> menuList = menuMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(menuList)) {
            return Collections.emptyList();
        }
        ArrayList<MenuVo> list = Lists.newArrayListWithCapacity(menuList.size());
        MenuVo vo;
        for (MenuEntity eo : menuList) {
            vo = new MenuVo();
            BeanUtils.copyProperties(eo, vo);
            list.add(vo);
        }
        return list;
    }

    @Override
    public List<MenuVo> getParentList(MenuDto dto) {
        List<Integer> levelList = Arrays.asList(1);
        LambdaQueryWrapper<MenuEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(MenuEntity::getMenuLevel, levelList);
        List<MenuEntity> allList = menuMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(allList)) {
            return buildRootMenuList();
        }
        MenuVo vo;
        List<MenuVo> resultList = Lists.newArrayListWithCapacity(allList.size() + 1);
        resultList.addAll(buildRootMenuList());
        for (MenuEntity eo : allList) {
            vo = new MenuVo();
            BeanUtils.copyProperties(eo, vo);
            resultList.add(vo);
        }
        return resultList;
    }

    private List<MenuVo> buildRootMenuList() {
        List<MenuVo> list = Lists.newArrayListWithCapacity(1);
        MenuVo menuVo = new MenuVo();
        menuVo.setId(0L);
        menuVo.setMenuName("根菜单");
        menuVo.setMenuLevel(0);
        list.add(menuVo);
        return list;
    }

    @Override
    public List<MenuTreeVo> menuTreeList(MenuDto dto) {
        LambdaQueryWrapper<MenuEntity> queryWrapper = new LambdaQueryWrapper<>();
        List<MenuEntity> allList = menuMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(allList)) {
            return Collections.emptyList();
        }
        List<MenuEntity> firstList = allList.stream().filter(s -> (!ObjectUtils.isEmpty(s.getMenuLevel()) && s.getMenuLevel() == 1)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(firstList)) {
            return Collections.emptyList();
        }
        List<MenuEntity> secondList = allList.stream().filter(s -> (!ObjectUtils.isEmpty(s.getMenuLevel()) && s.getMenuLevel() == 2)).collect(Collectors.toList());
        Map<Long, List<MenuEntity>> pidMap = secondList.stream().collect(Collectors.groupingBy(MenuEntity::getPid));
        List<MenuTreeVo> resultList = Lists.newArrayListWithCapacity(firstList.size());
        MenuTreeVo menuTreeVo;
        for (MenuEntity first :firstList) {
            menuTreeVo = new MenuTreeVo();
            menuTreeVo.setId(first.getId());
            menuTreeVo.setLabel(first.getMenuName());
            List<MenuEntity> pidSecondList = pidMap.get(first.getId());
            if (CollectionUtils.isEmpty(pidSecondList)) {
                resultList.add(menuTreeVo);
                continue;
            }
            MenuTreeVo secondMenuTreeVo;
            List<MenuTreeVo> secondResultList = Lists.newArrayListWithCapacity(pidSecondList.size());
            for (MenuEntity second : pidSecondList) {
                secondMenuTreeVo = new MenuTreeVo();
                secondMenuTreeVo.setId(second.getId());
                secondMenuTreeVo.setLabel(second.getMenuName());
                secondResultList.add(secondMenuTreeVo);
            }
            menuTreeVo.setChildren(secondResultList);
            resultList.add(menuTreeVo);
        }
        return resultList;
    }

    @Override
    public List<LeftMenuTreeVo> getLeftTreeList(MenuDto dto) {
        LambdaQueryWrapper<MenuEntity> queryWrapper = new LambdaQueryWrapper<>();
        List<MenuEntity> allList = menuMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(allList)) {
            return Collections.emptyList();
        }
        List<MenuEntity> firstList = allList.stream().filter(s -> (!ObjectUtils.isEmpty(s.getMenuLevel()) && s.getMenuLevel() == 1)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(firstList)) {
            return Collections.emptyList();
        }
        List<MenuEntity> secondList = allList.stream().filter(s -> (!ObjectUtils.isEmpty(s.getMenuLevel()) && s.getMenuLevel() == 2)).collect(Collectors.toList());
        Map<Long, List<MenuEntity>> pidMap = secondList.stream().collect(Collectors.groupingBy(MenuEntity::getPid));
        List<LeftMenuTreeVo> resultList = Lists.newArrayListWithCapacity(firstList.size());
        LeftMenuTreeVo menuTreeVo;
        for (MenuEntity first :firstList) {
            menuTreeVo = new LeftMenuTreeVo();
            BeanUtils.copyProperties(first, menuTreeVo);
            menuTreeVo.setMenuId(first.getId());
            List<MenuEntity> pidSecondList = pidMap.get(first.getId());
            if (CollectionUtils.isEmpty(pidSecondList)) {
                resultList.add(menuTreeVo);
                continue;
            }
            LeftMenuTreeVo secondMenuTreeVo;
            List<LeftMenuTreeVo> secondResultList = Lists.newArrayListWithCapacity(pidSecondList.size());
            for (MenuEntity second :pidSecondList) {
                secondMenuTreeVo = new LeftMenuTreeVo();
                BeanUtils.copyProperties(second, secondMenuTreeVo);
                secondMenuTreeVo.setMenuId(second.getId());
                secondResultList.add(secondMenuTreeVo);
            }
            menuTreeVo.setMenus(secondResultList);
            resultList.add(menuTreeVo);
        }
        return resultList;
    }
}
